/*
 * T.Raynal - A.Aviles - F.Diallo
 * XML-Scenarios
 * PPA AL5-2
 * December 2012 
 */
package model;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import common.Commons.STATE;
import common.exception.EvaluationException;
import model.scenario.UnregisteredComponentException;
import model.serialization.*;
import model.serialization.XmlArray.ELEMENT;
import model.serialization.XmlArray.STRUCTURE;


// TODO: Auto-generated Javadoc
/**
 * Represent a scenario.
 */
@XmlElement(name = "SCENARIO")
public class Scenario implements XmlSerializable, Serializable  
{
	
	/** The Constant serialVersionUID. */
	private static final long serialVersionUID = 1L;

	/** Scenario's alternatives */
	@XmlArray(name ="SITUATIONS", hasContainer=false, structure=STRUCTURE.HASH_MAP, type=ELEMENT.SITUATION) 
	public HashMap<String,Situation> _map;
	
	/**Scenario name*/
	@XmlAttribute (name="name")
	public String _name;
	
	private Situation _currentSituation;
	private Situation _startSituation;
	
	/**
	 * Instantiates a new scenario.
	 */
	public Scenario ()
	{
		_map = new HashMap<>();
	}
	
	/**
	 * Load a situation.
	 *
	 * @param Situation
	 */
	public void put(Situation situation)
	{
		if (null ==_currentSituation && _map.size()==0)
		{
			_currentSituation  = situation;
			_startSituation = situation;
		}
		_map.put(situation.Label, situation);
	}
	
	/**
	 * Gets a situation from an id.
	 *
	 * @param String
	 * @return Situation
	 * @throws UnregisteredComponentException
	 */
	public Situation get (String id) throws UnregisteredComponentException
	{
		Situation result = null;
		
	
		
		if(!(id==""))
		{
			if ((result = _map.get(id))== null)
			{
				throw new UnregisteredComponentException(id);
			}
		}
		
		return result;
	}
	
	/**
	 * Get the next Situation to be evaluate.
	 *
	 * @return Situation
	 * @throws EvaluationException
	 */
	public Situation next() throws EvaluationException {
		
		Situation result = _currentSituation;
		
		if (_currentSituation.state()==STATE.PROCEEDED)
		{
				try
				{
					result = get(_currentSituation.yield());
					
					if (null == result || result.state()==STATE.PROCEEDED)
					{
						result = null;
					}
						
					
				}
				catch (Exception e)
				{
					throw new EvaluationException(e.getMessage());
				}
		}
		
		
			_currentSituation = result;
		
			return result;
	}
		
	/**
	 * Reset the scenario.
	 */
	public void reset() {
		Iterator<Entry<String, Situation>> iterator = _map.entrySet().iterator();
	    while (iterator.hasNext()) {
	    	iterator.next()
	    		.getValue()
	    			.reset();
	    }
	    _currentSituation = _startSituation;
	}

	/**
	 * Iterator on the scenarios alternatives.
	 *
	 * @return Iterator
	 */
	public Iterator<Situation> iterator() {
		return _map.values().iterator();
	}

	/* (non-Javadoc)
	 * @see model.serialization.XmlSerializable#id()
	 */
	@Override
	public String id() {
		return _name;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object o)
	{
		boolean result =false;
		
		if (o.getClass()==Scenario.class)
		{
			if (((Scenario)o).id().equals(id()))
				result = true;
		}
		
		return result;
	}

	/**
	 * Yield of the scenario.
	 *
	 * @return Boolean
	 */
	public Boolean yield() {
		
		try {
			return (null== next())? true
									: false;
		} catch (EvaluationException e) {
			return false;
		}			
	}

	/**
	 * Get the current situation.
	 *
	 * @return Situation
	 * @throws EvaluationException
	 */
	public Situation current() throws EvaluationException {
		return _currentSituation;
	}
	

	/**
	 * Save the scenario current state.
	 *
	 * @param String
	 * @throws IOException
	 */
	public void save(String name) throws IOException
	{
		FileOutputStream fos = new FileOutputStream(name);
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		oos.writeObject(this); 
		
		oos.flush();
		oos.close();
		fos.close();
	}

}
